package com.liziguo.lzgfp.server.service;

import com.alibaba.fastjson.JSONObject;
import com.liziguo.lzgfp.server.dao.ForwardDao;
import com.liziguo.lzgfp.server.dao.UserDao;
import com.liziguo.lzgfp.server.domain.ClientView;
import com.liziguo.lzgfp.server.domain.ForwardView;
import com.liziguo.lzgfp.server.domain.Result;
import com.liziguo.lzgfp.server.entity.Forward;
import com.liziguo.lzgfp.server.entity.User;
import com.liziguo.lzgfp.server.netty.Server;
import com.liziguo.lzgfp.server.util.GlobalUtil;
import io.netty.channel.Channel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ContentDisposition;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @Author: Liziguo
 * @Date: 2021/12/25 21:24
 */
@Service
public class AdminService {

    private final Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    private Server server;

    private List<Forward> stringToForWards(int userId, String forwards) throws Exception {
        List<Forward> list = new ArrayList<>();
        for (String forward : forwards.split(",")) {
            if (!forward.isBlank()) {
                String[] split = forward.split(":");
                if (split.length != 3) {
                    throw new Exception("输入的转发地址有误");
                }
                int serverport = -1;
                try {
                    serverport = Integer.parseInt(split[0].strip());
                } catch (NumberFormatException e) {
                }
                if (serverport < 0 || serverport > 65535) {
                    throw new Exception("server端口有误");
                }
                if (split[1].isBlank()) {
                    throw new Exception("请输入本地主机地址");
                }
                int localport = -1;
                try {
                    localport = Integer.parseInt(split[2].strip());
                } catch (NumberFormatException e) {
                }
                if (localport < 0 || localport > 65535) {
                    throw new Exception("server端口有误");
                }
                Forward insert = new Forward();
                insert.setUserId(userId);
                insert.setServerport(serverport);
                insert.setLocalhost(split[1].strip());
                insert.setLocalport(localport);
                list.add(insert);
            }
        }
        return list;
    }

    public ClientView getClientView(String key) {
        final User user = UserDao.selectByKey(key);
        if (user == null) throw new RuntimeException("key不存在");
        final List<ForwardView> forwardViews = ForwardDao.selectByUserId(user.getId()).stream().map(ForwardView::create).collect(Collectors.toList());
        return new ClientView(user.getKey(), user.getRemark(), GlobalUtil.client.get(key) != null, forwardViews, user.getCreateTime());
    }

    public Result getView(String key) {
        try {
            return Result.success(getClientView(key));
        } catch (Exception e) {
            return Result.fail(e.getMessage());
        }
    }

    public List<ClientView> list() {
        final List<ClientView> views = new ArrayList<>();
        for (User user : UserDao.selectAll()) {
            views.add(getClientView(user.getKey()));
        }
        views.sort((o1, o2) -> {
            if (o1.isOnline() == o2.isOnline()) {
                return 0;
            } else {
                if (o1.isOnline()) {
                    return -1;
                } else {
                    return 1;
                }
            }
        });
        return views;
    }

    @Transactional
    public Result update(String key, String remark, String forwards) {
        if (key == null || key.isBlank()) return Result.fail("key不能为空");
        if (forwards == null || forwards.isBlank()) return Result.fail("请添加转发");
        User user = UserDao.selectByKey(key);
        if (user == null) return Result.fail("key不存在");
        user.setRemark(remark);
        ForwardDao.deleteByUserId(user.getId());
        forwards = forwards.replace('：', ':');
        List<Forward> insertList = null;
        try {
            insertList = stringToForWards(user.getId(), forwards);
        } catch (Exception e) {
            return Result.fail(e.getMessage());
        }
        for (Forward forward : insertList) {
            try {
                ForwardDao.insert(forward);
            } catch (Exception e) {
                return Result.fail(String.format("插入 %d -> %s:%d 时出现错误 端口%d可能已存在", forward.getServerport(), forward.getLocalhost(), forward.getLocalport(), forward.getServerport()));
            }
        }
        UserDao.updateByKey(user);
        this.disconnect(key, 1);
        return Result.success();
    }

    @Transactional
    public Result insert(String key, String remark, String forwards) {
        if (key == null || key.isBlank()) return Result.fail("key不能为空");
        if (forwards == null || forwards.isBlank()) return Result.fail("请添加转发");
        User user = UserDao.selectByKey(key);
        if (user != null) return Result.fail("key:" + key + "已存在");
        if (UserDao.insert(key) != 1) return Result.fail("插入数据库失败");
        user = UserDao.selectByKey(key);
        if (user == null) return Result.fail("插入数据库失败");
        user.setRemark(remark);
        ForwardDao.deleteByUserId(user.getId());
        forwards = forwards.replace('：', ':');
        List<Forward> insertList = null;
        try {
            insertList = stringToForWards(user.getId(), forwards);
        } catch (Exception e) {
            return Result.fail(e.getMessage());
        }
        for (Forward forward : insertList) {
            try {
                ForwardDao.insert(forward);
            } catch (Exception e) {
                return Result.fail(String.format("插入 %d -> %s:%d 时出现错误 端口%d可能已存在", forward.getServerport(), forward.getLocalhost(), forward.getLocalport(), forward.getServerport()));
            }
        }
        UserDao.updateByKey(user);
        Channel channel = GlobalUtil.client.get(user.getKey());
        if (channel != null) channel.close();
        return Result.success(null, "添加成功");
    }

    @Transactional
    public Result delete(String key) {
        if (key == null || key.isBlank()) return Result.fail("key不能为空");
        User user = UserDao.selectByKey(key);
        if (user == null) return Result.fail("用户不存在");
        Channel channel = GlobalUtil.client.remove(key);
        if (channel != null) channel.close();
        UserDao.delete(user.getId());
        ForwardDao.deleteByUserId(user.getId());
        return Result.success();
    }

    /**
     * @param key
     * @param s   指定下一次重连时间 单位秒 为负数的话客户端将直接退出程序
     * @return
     */
    public Result disconnect(String key, int s) {
        Channel channel = GlobalUtil.client.get(key);
        if (channel == null) return Result.fail("客户端不在线");
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("type", "disconnect");
        jsonObject.put("time", s);
        channel.writeAndFlush(jsonObject);
        return Result.success("发送指令成功");
    }

    public Result forcedDisconnect(String key) {
        Channel channel = GlobalUtil.client.get(key);
        if (channel == null) return Result.fail("客户端不在线");
        channel.close();
        return Result.success();
    }

    public ResponseEntity<byte[]> downloadClient() {
        File[] files = new File(System.getProperty("user.dir")).listFiles();
        if (files == null) {
            throw new RuntimeException();
        }
        File file = null;
        for (File f : files) {
            String name = f.getName();
            if (name.endsWith(".jar") && name.contains("client")) {
                file = f;
                break;
            }
        }
        if (file == null) {
            throw new RuntimeException();
        }
        byte[] bytes = null;
        try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file))) {
            bytes = bis.readAllBytes();
        } catch (IOException e) {
            e.printStackTrace();
        }

        if (bytes == null) {
            throw new RuntimeException();
        }

        HttpHeaders headers = new HttpHeaders();
        headers.setContentDisposition(ContentDisposition.attachment().filename(file.getName(), StandardCharsets.UTF_8).build());
        return ResponseEntity.ok().headers(headers).contentType(MediaType.parseMediaType("application/octet-stream")).contentLength(bytes.length).body(bytes);
    }

}
